/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * Projects
 * Copyright (C) Arnaud Duval 2010 <arnaud.duval@gmail.com>
 * 
 * Projects is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Projects is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <Duration.hpp>
#include <sstream>


///Default constructor
Duration::Duration()
{
    hour = 0;
    minute = 0;
    second = 0;
}

///Destructor
Duration::~Duration()
{

}

///@fn Duration::operator=(const Duration& d)
///@brief Overload of = operator
///@param d right hand operand
Duration& Duration::operator=(const Duration& d)
{
    hour = d.hour;
    minute = d.minute;
    second = d.second;
    return (*this);
}

//Return duration value in seconds
double Duration::ValueInSeconds() const
{
    return (double)(hour*3600. + minute*60. + second);
}

//Return duration value in minutes
double Duration::ValueInMinutes() const
{
    return (double)(hour*60. + minute + (double)(second)/60.);
}

//Return duration value in hours
double Duration::ValueInHours() const
{
    return (double)(hour + (double)(minute)/60. + (double)(minute)/3600.);
}

///@brief Normalize the duration value
void Duration::Normalize()
{
    second = second + 60*minute + 3600*hour;
    hour = second / 3600;
    minute = (second % 3600)/60;
    second = (second % 3600)%60;
}

///@brief Set current Duration to the difference between two DateTime
///@param d1 First date (should be higher than second date)
///@param d2 Second date
void Duration::Difference(DateTime d1, DateTime d2)
{
    int dayDifference = d1.NumberOfDays() - d2.NumberOfDays();
    if(dayDifference != 0)
    {
        second = ((dayDifference-1)*86400);
        second += (d1.GetHour()*3600 + d1.GetMinute()*60 + d1.GetSecond());
        second += (60 - d2.GetSecond() + (59 - d2.GetMinute())*60 + (23 - d2.GetMinute())*3600);
    }
    else
    {
        second = (d1.GetHour()*3600 + d1.GetMinute()*60 + d1.GetSecond()) - (d2.GetHour()*3600 + d2.GetMinute()*60 + d2.GetSecond());
    }
    Normalize();
}

///@brief Overload of += operator
///@param d Duration object to add to current object
void Duration::operator += (const Duration& d)
{
    hour += d.hour;
    minute += d.minute;
    second += d.second;
    Normalize();
}

///@brief Return hour value
unsigned int Duration::GetHour()
{
    return hour;
}

///@brief Return minute value
unsigned int Duration::GetMinute()
{
    return minute;
}

///@brief Return second value
unsigned int Duration::GetSecond()
{
    return second;
}

///@brief Return duration as text
std::string Duration::AsText()
{
    std::string res = "";
    std::ostringstream streamHour;
    streamHour << hour;
    res += streamHour.str();
    res += ":";
    if(minute < 10)
        res += "0";
    std::ostringstream streamMinute;
    streamMinute << minute;
    res += streamMinute.str();
    res += ":";
    if(second < 10)
        res += "0";
    std::ostringstream streamSecond;
    streamSecond << second;
    res += streamSecond.str();
    return res;
}

///@brief Set the DateTime object from a string with given format
///@param t String containing the DateTime value
///@param f String containing the format
void Duration::SetWithFormat(std::string t, std::string f)
{
    if(t.length() != f.length())
        return;
    size_t found;

    //Looking for "h" string
    found = f.find("h");
    if(found != std::string::npos)
    {
        std::string hourString = t.substr((int)found, 1);
        std::istringstream stream(hourString);
        stream >> hour;
    }

    //Looking for "nn" string
    found = f.find("nn");
    if(found != std::string::npos)
    {
        std::string minuteString = t.substr((int)found, 2);
        std::istringstream stream(minuteString);
        stream >> minute;
    }


    //Looking for "ss" string
    found = f.find("ss");
    if(found != std::string::npos)
    {
        std::string secondString = t.substr((int)found, 2);
        std::istringstream stream(secondString);
        stream >> second;
    }
}


///@brief Set the hour value
///@param Dhour Hour value to be set
void Duration::SetHour(unsigned int Dhour)
{
    hour = Dhour;
}

///@brief Set the minute value
///@param Dminute Minute value to be set
void Duration::SetMinute(unsigned int Dminute)
{
    minute = Dminute;
}

///@brief Set the second value
///@param Dsecond Second value to be set
void Duration::SetSecond(unsigned int Dsecond)
{
    second = Dsecond;
}

///@brief Reset duration value
void Duration::Reset()
{
    second = 0;
    minute = 0;
    hour = 0;
}

///@brief Overload of << operator
///@param s out stream
///@param d Duration object to put in the out stream
std::ostream& operator<<(std::ostream & s, const Duration& d)
{
    s << d.hour << ":";
    if(d.minute < 10)
        s << "0";
    s << d.minute << ":";
    if(d.second < 10)
        s << "0";
    s << d.second;
    
    return s;
}

///@brief Overload of > operator
///@param d1 left hand operand
///@param d2 right hand operand
bool operator>(const Duration& d1, const Duration& d2)
{
    if(d1.hour > d2.hour)
        return true;
    if(d1.hour < d2.hour)
        return false;

    if(d1.minute > d2.minute)
        return true;
    if(d1.minute < d2.minute)
        return false;

    if(d1.second > d2.second)
        return true;
    if(d1.second < d2.second)
        return false;

    return false;
}

///@brief Overload of + operator : addition of a Duration object to a DateTime object
///@param date DateTime object
///@param duration Duration object to be added to date
DateTime operator+(const DateTime& date, const Duration& duration)
{
    DateTime res = date;
    double secondsToAdd = duration.ValueInSeconds();
    res.SetSecond(res.GetSecond() + (unsigned int)secondsToAdd);
    res.Normalize();
    return res;
}
